11.8 Using AspectJ with Spring applications

我们在以前章节所讲的内容都是纯粹的Spring AOP。本章假设您的需求超出了Spring AOP提供的功能, 进而讨论如何使用AspectJ编译器/编织器,而不是Spring AOP。
Spring附带一个小的AspectJ切面库,它在Spring的发行版中是独立的,即Spring-aspects.jar;你需要将这个jar添加到你的classpath下,以保证你可以使用AspectJ的切面功能。11.8.1节,"Using AspectJ to dependency inject domain objects with Spring"11.8.2节,"Other Spring aspects for AspectJ"讨论了这个库里的内容和你要如何去使用。11.8.3节,"Configuring AspectJ aspects using Spring IoC"讨论如何注入由AspectJ编译器织入的Aspect切面。11.8.4节,"Load-time weaving with AspectJ in the Spring Framework"提供了Spring应用使用AspectJ在加载时织入的介绍。

11.8.1 Using AspectJ to dependency inject domain objects with Srping

Spring容器可以对定义在你应用程序上下文中的bean初始化和配置。也可以让bean工厂根据包含配置信息bean的名称,找到预先存在的对象进行配置。spring-aspects.jar包含一个注解驱动的切面,利用这一功能允许注入到任何一个对象。该功能用于任何由Spring容器控制外创建的对象。domain对象通常被归为这类,因为他们一般new操作创建或被一个ORM工具作为数据库查询结果集而创建。
@Configurable注解标记的类视为符合Spring驱动配置的类。下面的简单例子中,@Configurable仅作为标记注解:

package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class Account {
    // ...
}

当作为一个标记接口来使用时,Spring将为被标记的类型的配置新实例(本例是Account), 使用该类型的全定限制名( com.xyz.myapp.domain.Account)作为该类型的bean定义名称(该类型bean的作用域通常为原型类型)。因为bean的默认名称是其类型的全限定名称,声明原型类型bean的一种简便的方法是省略其id属性:

<bean class="com.xyz.myapp.domain.Account" scope="prototype">
    <property name="fundsTransferService" ref="fundsTransferService"/>
</bean>

如果你想显式的指定原型定义bean的名称,你可以在注解中直接声明。

package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;


@Configurable("account")
public class Account {
    // ...
}

指定名称后,Spring将找寻一个名称为"account"的bean,并使用这个bean来配置新的Account实例。
你也可以使用自动注入避免指定一个专用的bean定义。 为了Spring能够应用自动注入,你需要设置@Configurable注解的autowire属性, @Configurable(autowire=Autowire.BY_TYPE), @Configurable(autowire=Autowire.BY_NAM) 分别代表按类型注入和按名称注入。另外,从Spring 2.5开始,最好通过在字段或方法级别使用@Autowired@Inject来为@Configurable bean指定显式注解驱动的依赖注入(更多信息,请查看7.9节,"Annotation-based container configuration")。
最后你也能够通过dependencyCheck属性为新创建和配置的对象开启Spring依赖检查(比如:@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true))。如果这个属性设置为true,Spring将在配置后检验所有的属性是否被设置(被校验的属性不包括基本类型和集合类型)。
注解本身并没有任何作用。spring-aspects.jar中的AnnotationBeanConfigurerAspect实现了实际上注解的作用。本质上说,“在一个被 @Configurable标记的类型初始化后,Spring会根据被注解的属性对这个新的对象进行配置。” 在这里,初始化是指实例化新的对象(例如,对象被new操作符创建)以及正在进行反序列化的Serializable对象(例如:通过readResolve())。

上面的段落描述的关键词是“本质”。大多数情况下,“在一个新对象初始化返回后”的语言是明确的...在这里,“初始化后” 意味着对象被构造后依赖开始注入——这意味着依赖不能再类的构造器中使用。如果你希望依赖在构造器执行前被注入,并且依赖可以在构造器中使用,那么你需要像这样定义@Configruable@Configurable(preConstruction=true)在AspectJ编程指南附录AspectJ部分,您可以找到关于各种切点类型的语法的详细信息。)
为了让它工作,注解类型必须被AspectJ编织器织入——你可以在编译时通过用Ant或是Maven实现(见AspectJ Development Enviroment Guide)或是在加载时织入(见11.8.4节,"Load-time weaving with AspectJ in the Spring Framework"。AnnotationBeanConfigurerAspect 本身需要Spring进行配置(为了获取一个Bean工厂的引用,该Bean工厂用于配置新对象)。 如果你使用基于Java的配置,仅需在使用@Configuration的类上加入@EnableSpringConfigured注解。

@Configuration
@EnableSpringConfigured
public class AppConfig {

}

如果你使用基于XML的配置,你需要在Springcontext命名空间中定义一个context:spring-configured元素:

<context:spring-configured/>

在切面创建之前创建的@Configurable对象实例,会导致向调试日志发出消息,并且不会进行任何配置。可能有一种情况是Spring配置的bean在由Spring初始化的时候创建了一个domain对象。在这种情况中,你可以使用bean的"depends-on"属性来手动指定, bean依赖的配置切面。

<bean id="myService"
        class="com.xzy.myapp.service.MyService"
        depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">

    <!-- ... -->

</bean>

不要通过激活@Configurable处理bean配置切面,除非你真的想是在运行时依赖它。特别要注意的是,要确保在被注册为常规Spring bean的类型中不要使用@Configurable 否则,您将得到两次初始化,一次是通过容器,一次是通过切面。

Unit testing @Configurable objects

@Configurable支持的目标之一就是支持domain对象的独立单元测试,而不会遇到硬编码查找带来的困难。 如果@Configurable标注的类型没有被AspectJ织入,在测试过程中该注解没有任何影响, 你可以简单地在测试对象中设置mock或stub属性引用,就像往常一样。 如果@Configurable标注的类型被AspectJ编入,你依旧可以像往常一样进行单元测试,但是你每次构建一个@Configurable标注的对象时将看到一个的警告信息 表明它还没有被Spring配置。

Working with multiple application contexts

AnnotationBeanConfigurerAspect@Configurable的支持实现是生成一个单例的AspectJ切面。 单例切面的作用域与静态变量的作用域一致,也就是说,一个类加载器下只有一个切面实例类型。 这意味着如果在相同的类加载器层次结构中定义多个应用程序上下文,则需要考虑在何处定义@EnableSpringConfiguredbean以及将spring-aspects.jar放置在类路径中的何处。
考虑一个包含一个典型的Spring Web应用程序配置,其中包含一个共享的父应用程序上下文,用于定义公共业务服务以及支持它们的所有事务,以及每个包含特定于该servlet的定义的一个子应用程序上下文。所有这些上下文将在同一个类加载器层次结构中共存,因此AnnotationBeanConfigurerAspect只能拥有对其中一个的引用。在这种情况下,我们建议在共享(父)应用程序上下文中定义@EnableSpringConfigured bean:这定义了你可能希望 注入到domain对象中的服务。后果是你不能使用@Configurable机制来配置子(特定servlet的)上下文的定义的bean的domain对象(这可能不是你想要的结果!)。
当在同一个容器中部署多个web-apps时,确保每一个web应用的类加载器均加载了spring-aspects.jar (例如,将spring-aspects.jar放到'WEB-INF/lib') 如果spring-aspects.jar只被添加到容器范围的类路径下(因此被shared父类加载器加载),所有web应用将共享同一个切面实例,这可能不是你想要的。

11.8.2 Other Spring aspectes for AspectJ

除了@Configurable切面,在spring-aspects.jar中还有另外一个切面。 这个切面可以驱动Spring事务管理,,用于注解@Transactional注解的类型和方法。这主要是为那些希望在Spring容器之外使用Spring框架的事务支持的用户而设计的。
AnnotationTransactionAspect解析@Transactional注释的切面。当使用这个切面时,你必须注解接口的实现类,而不是接口。AspectJ 遵循Java规则,接口上的注解不会被继承。
若一个@Transactional注解在一个类上,则这个类的所有公开方法均被事务管理。
若类的方法上存在一个@Transactional注释,它将覆盖由类注解给出(如果存在的话)的默认事务语义。任何可见方法均可被注解,也包含私有方法。 直接对非公开方法进行注解是获取此类方法的事务界定的唯一方法。

Spring框架4.2以后,spring-aspects提供了一个与标准javax.transaction.Transactional特性类似的切面。 更多细节,请查看JtaAnnotationTransactionAspect

对于希望使用Spring配置和事务管理支持但不希望(或不能)使用注释的AspectJ程序员,spring-aspects.jar还包含可以扩展以提供自己的切点定义的抽象切面。更多信息,请查看AbstractBeanConfigurerAspectAbstractTransactionAspect源码。 作为一个例子,下面的摘录展示了:写一个切面,使用原型方式定义bean,以全定限名匹配去配置所有domain对象。

public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect {

    public DomainObjectConfiguration() {
        setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());
    }

    // the creation of a new bean (any object in the domain model)
    protected pointcut beanCreation(Object beanInstance) :
        initialization(new(..)) &&
        SystemArchitecture.inDomainModel() &&
        this(beanInstance);

}

11.8.3 Configuring AspectJ aspects using Spring IoC

当Spring项目使用Aspect切面时,很自然的想法是能够利用Spring去配置切面。运行时,AspectJ本身负责创建切面,通过Spring配置AspectJ创建切面的需要通过切面使用的AspectJ实例化模型(per-xxx子句)。
大多数AspectJ切面都是单例切面。 配置单例切面是非常简单的:创建一个切面类型的bean定义跟普通bean一样,仅需在bean定义中包含'factory-method="aspectOf"'属性。该属性确保Spring从AspectJ中获取切面实例而不是自己尝试创建一个切面实例,请看下例:

<bean id="profiler" class="com.xyz.profiler.Profiler"
        factory-method="aspectOf">

    <property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>

非单例切面配置较为困难。但是通过创建prototype的bean定义并使用spring-aspects.jar中的@Configurable支持来配置AspectJ运行时创建的bean的方面实例是可能的。
如果你有一些@AspectJ切面。它们都是有Spring配置的,其中一些你希望由AspectJ织入(比如,在加载时织入domain的模型类型),另一些你希望使用Spring AOP,那么你需要告诉Spring AOP @Aspect自动代理为配置中的哪些@AspectJ切面提供自定代理。你可以通过在<aop:aspectj-autoproxy>声明中使用一个或多个<include/>元素实现。每一个<include/>元素指明了一个命名样式,只有名字至少符合一个样式的bean才会被应用Spring AOP自动代理配置:

<aop:aspectj-autoproxy>
    <aop:include name="thisBean"/>
    <aop:include name="thatBean"/>
</aop:aspectj-autoproxy>

不要被<aop:aspectj-autoproxy/>的名字所误导:使用它会导致创建Spring AOP代理。@AspectJ风格的切面声明就介绍到这,但spectJ运行部分还没有涉及。

11.8.4 Load-time weaving with AspectJ in the Spring Framework

加载时织入(LTW)是指在切面被加载到Java虚拟机(JVM)时,将切面织入到应用程序的类文件的过程。本节的重点在特定的Spring框架的上下文中使用和配置LTW:而不是介绍LTW。关于LTW的特性和只在AspectJ是配置LTW(不涉及Spring),请见LTW section of the AspectJ Development Enviroment Guide
Spring框架为Aspect LTW带来的附加价值是对织入过程的控制更加精细。"Vanilla" AspectJ LTW是由Java(5+)代理实现的,通过在启动JVM的时候打开特定的VM参数。因此,这是JVM级别的设置,在某些情况下这可能适合,但是大部分情况来说这太粗粒度了。结合Spring的LTW允许你基于每个类加载器打开LTW,这明显更细粒度,并且在"单虚拟机-多应用的"环境下更有意义(比如典型的应用程序服务器环境)。
此外,在某些环境下,这种支持可以在不修改应用程序服务器启动脚本的情况下进行加载时织入,(一般情况下启动)这需要添加-javaagent:path/to/aspectjweaver.jar或是(正如我们在这节之后介绍的)-javaagent:path/to/org.springframework.instrument-{version}.jar(以前称作spring-agent.jar)。开发者只需要简单的修改应用程序上下文中的一个或多个文件就可以启用加载时织入,而不用依赖管理员(通常管理发布部署)的启动脚本。
好了,销售环节到此为止,让我们先通过一个例子简单的了解一下结合Spring使用AspectJ LTW,然后再详细介绍下面例子中的元素。完整的例子,请见Petclinic sample application

A first example

我们假设你是一个应用程序的开发者,负责诊断系统的性能问题。我们需要做的是简单的打开一个剖析切面获得一些性能指标,而不是去使用分析工具,因此我们能够将更细粒度的分析工具立即应用到特定的区域。

这里的例子使用XML风格的配置,用Java风格来配置和使用@AspectJ也是可以的。特别的,@EnableLoadTimeWeaving注解可以作为<context:load-time-weaver/>详见)。
下面是分析的切面。没有太多花哨的东西,仅仅是简陋的基于时间的,用@AspectJ风格声明的切面所表示的分析器。

package foo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
import org.springframework.core.annotation.Order;

@Aspect
public class ProfilingAspect {

    @Around("methodsToBeProfiled()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        StopWatch sw = new StopWatch(getClass().getSimpleName());
        try {
            sw.start(pjp.getSignature().getName());
            return pjp.proceed();
        } finally {
            sw.stop();
            System.out.println(sw.prettyPrint());
        }
    }

    @Pointcut("execution(public * foo..*.*(..))")
    public void methodsToBeProfiled(){}
}

我们还需要创建一个META-INF/aop.xml文件来通知AspectJ编织器把ProfilingAspect织入我们的类中。标准的AspectJ对于这个文件的约定是:Java类路径下一个或多个叫做META-INF/aop.xml的文件。

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver>
        <!-- only weave classes in our application-specific packages -->
        <include within="foo.*"/>
    </weaver>

    <aspects>
        <!-- weave in just this aspect -->
        <aspect name="foo.ProfilingAspect"/>
    </aspects>

</aspectj>

现在配置Spring的特定部分。我们需要配置一个LoadTimeWeaver(会在稍后讲解,先拿来用)。运行时织入器是负责将配置在一个或多个META-INF/aop.XML的切面织入到你的应用程序中的类中必不可少的组件。好消息是这不需要太多的配置,正如下面所见的(还有另外一些选项你可以设置,之后再讲)。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- a service object; we will be profiling its methods -->
    <bean id="entitlementCalculationService"
            class="foo.StubEntitlementCalculationService"/>

    <!-- this switches on the load-time weaving -->
    <context:load-time-weaver/>
</beans>

现在,所有必要的构建都已经到位了——切面,META-INF/aop.xml文件和Spring配置。让我们创建一个带有main(..)方法的驱动类来实际演示LTW。

package foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main {

    public static void main(String[] args) {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);

        EntitlementCalculationService entitlementCalculationService
            = (EntitlementCalculationService) ctx.getBean("entitlementCalculationService");

        // the profiling aspect is 'woven' around this method execution
        entitlementCalculationService.calculateEntitlement();
    }
}

还有最后一件事要做。本节的介绍中,我们说过结合Spring时,使用者以每个ClassLoader为基准选择性的开启LTW,这是真的。然而,就这个例子而言,我们是使用Java 代理(由Spring提供)来开启LTW。当我们在使用上面的Main类时,我们需要在命令行输入:
java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main
-javaagent是用来指出和开启代理去协助JVM运行的程序。。Spring框架提供了一个这样的代理,spring-instrument.jar包中的InstrumentationSavingAgent,这是它为上述例子的-javaagent参数提供了值。
Main程序的输出结果会像下面这样。(我在calculateEntitlement()的实现中引入了Thread.sleep(..),是为了让分析器可以实际获取点什么而不是0毫秒——01234毫秒不是引入AOP造成的花费:))

Calculating entitlement

StopWatch 'ProfilingAspect': running time (millis) = 1234
------ ----- ----------------------------
ms     %     Task name
------ ----- ----------------------------
01234  100%  calculateEntitlement

由于LTW是基于完全的AspectJ实现的,因此我们可以不局限于对Spring bean增加通知;下面的Main程序作了轻微的改变,但是仍输出相同的结果。

package foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main {

    public static void main(String[] args) {

        new ClassPathXmlApplicationContext("beans.xml", Main.class);

        EntitlementCalculationService entitlementCalculationService =
            new StubEntitlementCalculationService();

        // the profiling aspect will be 'woven' around this method execution
        entitlementCalculationService.calculateEntitlement();
    }
}

注意上面的程序我们是如何启动Spring容器,然后又在容器外创建了StubEntitlementCalculationService实例...但是分析切面任然织入到了其中。
这个例子非常简单...但是Spring对LTW提供支持的基本内容都已经介绍了,本节剩余部分将详细解释每一个配置和用法。

这个例子中的ProfilingAspect可能很基本,但是想当有用。但是对于开发者在开发时切面(在开发的时候使用)来说是个很好的例子,并且可以很容易的从部署到UAT或是生产环境的时候抽离。

Aspects

LTW所用的切面必须是AspectJ aspects。AspectJ切面要用AspectJ语言编写或是将你的切面用@AspectJ-风格的去编写。这意味着你的切面要对AspectJ和Spring AOP都是合法的。另外,被编译的切面类要在类路径上。

'META-INF/aop.xml'

AspectJ LTW结构配置了一个或多个META-INF/aop.xml文件,(直接,更通常在jar包里)存在在Java类路径下。
这个文件的结构和内容会在AspectJ的文档中主要介绍,感兴趣的读着可以查看。(我了解这节有点简短,但是aop.xml是100%的ApsectJ——没有应用Spring特定的信息和语法,因此也就没有额外介绍的价值),因此不在重写相关内容,推荐去看文档。

Required libraries(JARS)

为了让Spring Framework支持AspectJ LTW,你至少需要下列的库:
spring-aop.jar(2.5及以后的版本,并添加所有的依赖)
aspectjweaver.jar(1.6.8及以后的版本)
如果你使用了Spring-provided agent to enable instrument,你还需要:
* spring-instrument.jar

Spring configuration

Spring对LTW支持的关键组件是LoadTimeWeaver接口(在org.springframework.instrument.classloading包中),Spring提供了几个实现。LoadTimeWeaver负责在运行时添加一个或者多个java.lang.instrument.ClassFileTransformersClassLoader,这为各种有趣的应用打开了大门,而LTW的切面正是其中之一。

如果你对运行时类文件转换的概念不熟悉,建议你继续学习前先阅读java.lang.instrument的API文档。这并不是很繁琐的事,因为文档里有很多宝贵的信息,至少在阅读本节时,关键的接口和类的文档需要摆在你的面前。
为特定的ApplicationContext配置LoadTimeWeaver可以用一句话简单的完成。(请注意,你需要使用ApplicationContext作为Spring容器,通常BeanFactory是不够的,因为支持LTW是利用了BeanFactoryPostProcessors。)
为了开启Spring框架对LTW的支持,你需要配置一个LoadTimeWeaver,通常是用@EnableLoadTimeWeaving注解。

@Configuration
@EnableLoadTimeWeaving
public class AppConfig {

}

如果你偏爱基于XML的配置,也可以选择<context:load-time-weaver/>元素。注意,这个元素定义在context命名空间内。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:load-time-weaver/>

</beans>

上述的配置会为你自动注册一些LTW特定的基础beans,比如LoadTimeWeaverAspectJWeavingEnabler。默认的LoadTimeWeaverDefaultContextLoadTimeWeaver类,它会包装了一个自动检测的LoadTimeWeaver:这个被检测的LoadTimeWeaver的确切类型取决于你运行环境(在下表中做了总结)。
表11.1 DefaultContextLoadTimeWeaver LoadTimeWeavers

运行环境 LoadTimeWeaver的实现
在Oracle的WebLogic中运行 WebLogicLoadTimeWeaver
在Oracle的GlassFish中运行 GlassFishLoadTimeWeaver
在Apache Tomcat中运行 TomcatLoadTimeWeaver
在RedHat的JBoss AS或WildFly中运行 JBossLoadTimeWeaver
在IBM的WebSphere中运行 WebSphereLoadTimeWeaver
由SpringInstrumentationSavingAgent的启动的JVM(java -javaagent:/path/to/spirng-instrument.jar) InstrumentationLoadTimeWeaver
底层的ClassLoader遵循公约的(比如,适用于TomcatIntrumentableClassLoader和Resin) ReflectiveLoadTimeWeaver
注意这只是在使用DefaultContextLoadTimeWeaver时自动检测的LoadTimeWeavers:当然你也可以特指你希望使用的LoadTimeWeaver实现。
为了通过Java配置特指出LoadTimeWeaver,你需要实现LoadTimeWeavingConfigurer接口并重写getLoadTimeWeaver()方法:
@Configuration
@EnableLoadTimeWeaving
public class AppConfig implements LoadTimeWeavingConfigurer {

    @Override
    public LoadTimeWeaver getLoadTimeWeaver() {
        return new ReflectiveLoadTimeWeaver();
    }
}

如果你使用基于XML的配置,你可以将<context:load-time-weaver>元素的weaver-class属性的值设置编织器的类全限定名:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:load-time-weaver
            weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>

</beans>

配置中定义和注册的LoadTimeWeaver会在之后被Spring容器以loadTimeWeaver的名字检索。请记住LoadTimeWeaver是作为Spring LTW用来添加一个或者多个ClassFileTransformers的机制。真正执行LTW的ClassFileTransformerClassPreProcessorAgentAdapter(来子org.aspect.weaver.loadtime包)类。更多信息查看ClassPreProcessorAgentAdapater的文档,因为具体如何织入已经超越了本届的范畴。
还剩最后一个配置属性要介绍:aspectjWeaving属性(如果你使用XML属性的话,则是aspectj-weaving)。这个简单的属性用来控制是否开启LTW。它接受下面总结的三个属性,如果这个属性不存在的话,默认是autodetect
表11.2.AspectJ Weaving attribute values

注解值 XML值 说明符
ENABLED on 打开AspectJ织入,切面会在加载的时候适当的织入
DISABLED OFF 关闭LTW,不会有切面在加载时织入
AUTODETECT autodetect 如果Spring LTW结构可以找到至少一个META-INF/aop.xml文件,那么AspectJ织入会打开,否则则关闭。这是默认的值

Environment-specific configuration

最后一部分包含了诸如在应用程序服务器和web容器这样的环境中使用Spring的LTW支持时,你所需的俄爱的设置和配置。

Tomcat

从历史上看,Apache Tomcat默认的类加载器不支持类的转换,这就是为什么Spring提供了加强的实现来符合这个需求。它叫TomcatInstrumentableClassLoader,这个类加载器需要在Tomcat6.0及以上才工作。

在Tomcat 8.0以更高的版本中不用定义TomcatInstrumentableClassLoader。相反,让Springt通过TomcatLoadTimeWeaver策略自动的使用Tomcat的新的本地InstrumentableClassLoader

如果你仍然需要用TomcatInstrumentableClassLoader,可以像下面这样为每个web应用程序单独的注册:
复制org.springframework.instrument.tomcat.jar$CATALINA_HOME/lib下($CATALINA_HOME表示Tomcat安装的根目录) 通过编辑wb应用程序的上下文文件,指示Tomcat使用自定义的类加载器:

<Context path="/myWebApp" docBase="/my/webApp/location">
    <Loader
        loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
</Context>

Apache Tomcat(6.0+)支持多个路径下的上下文配置: 服务器配置文件——$CATALINA_HOME/conf/server.xml
默认的上下文配置——$CATALINA_HOME/conf/context.xml——这会影响所有不熟的web应用程序
* 为每个应用程序配置,可以是在服务部分的$CATALINA_HOME/conf/[enginename]/[hostname]/[webapp]-context.xml或是在每个应用程序内部的META-INF/context.xml
为了提高效率,建议使用潜入到每个应用程序的配置风格,因为这影响应用程序的类加载程序,而不会对服务器配置产生影响。更多信息查看Tomcat 6.0.x文档。
或者,考虑在Tomcat启动的脚本里指定用Spring提供的通用VM代理(见上面)。这使得所有发布的web应用程序都可以使用,而不管它们运行在哪个ClassLoader上。

WebLogic,WebSphere,Resin,GlassFish,JBoss

最近版本的WebLogic服务器(10及以后),IBM WebSphere应用服务器(7及以后),Resin(3.1及以后)和JBoss(6.x及以后)提供了一个可以instrument的类加载器。Spring本地LTW利用这种类加载器完成Aspect织入。你可以像我们直接说的那样很简单的激活加载时织入。特别的,你不需要在启动脚本中添加-javaagent:path/to/spring-instrument.jar
注意,GlassFish支持instrumentation的类加载器仅在EAR环境中可用。对于Glass web applications,参照上诉Tomcat设置说明。
注意在JBoss 6.x中,需要禁用应用程序服务器扫描,防止类在应用程序实际启动前加载。一个快速的解决办法是在你的项目中添加一个名为WEB-INF/jboss-scanning.xml,其中的内容如下:

<scanning xmlns="urn:jboss:scanning:1.0"/>

Generic Java applications

在不支持或者不被现有的LoadTimeWeaver实现所支持的环境中需要类instrumentation时,JDK代理可能时唯一的解决方案。对于这些情况,Spring提供了InstrumentationLoadTimeWeaver,这需要Spring特定(但是非常常见)的VM代理,org.springframework.instrument-{version}.jar(之前称作spring-agent.jar)。
要使用它,你需要用下面的配置在让Spring-agent和虚拟机一起启动:
-javaagent:/path/to/org.springframework.instrument-{version}.jar
注意,这需要修改VM启动的脚本,这可能在应用服务器z中被阻止(取决你的操作权限)。另外,JDK代理可能会instrument整个VM,这代价可能很昂贵。
出于性能原因,只有在目标环境(比如Jetty)没有(或不支持)专用的LTW时,才推荐使用此配置。